#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "cjson.h"

#include <linux/types.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include "fpga-tr700.h"
#include "printl.h"

#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

typedef unsigned char uint8_t;
typedef unsigned int  uint32_t;

#define MALLOC_LEN (64 * 1000 * 4 + 1000)
char *pbuf_tx_head;
char *pbuf_tx_cksum;
char *pbuf_tx_data;
char *pbuf_tx;
char *pbuf_rx;


#define DEF_SPI "/dev/spidev0.3"
static char     *device = DEF_SPI;
struct fpga_dev *dev;


struct fpga_encode
{
    char *head;
    int   len_head;
    int   cmd;

    char *cksum;
    int   len_cksum;


    char *tx;
    int   len_tx;
    char *rx;
    int   len_rx;
};
#define MIN_TX_PACK_SIZE (12)  // 最小发送帧长度
#define MIN_RX_PACK_SIZE (38)  // debug
static struct fpga_2016
{
    char head[5];
    /*
    FPGA 里忽略 glink 与 cmd 之间的 4 byte
    */

#if 1
    char len;
    char unuse0;
#else
    short len;
#endif
    char unuse1;
    char addr;
    char cmd;
} __attribute__((__packed__));

void PrintfMemory(uint8_t *buf, uint32_t len)
{
    for (uint32_t i = 0; i < len; i++) {
	if (i & 0xf) {
	    printf(" ");
	}
	else {
	    printf("\r\n");
	}
	printf("%2.2x", (unsigned char)*(buf + i));
    }
    printf("\r\n");
}

unsigned int this_fpga_bui2ld(struct fpga_dev    *dev,
                              struct fpga_encode *pencode)
{
    struct spi_ioc_transfer arr[] = {
	{
		.tx_buf      = (unsigned long)NULL,
		.rx_buf      = (unsigned long)NULL,
		.len         = 0,
		.cs_change   = 0,
		.delay_usecs = 100,
	},
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)pencode->rx,
		.len       = 0,
		.cs_change = 1,
	}
    };

    if (pencode->tx && pencode->len_tx) {
	arr[0].tx_buf = pencode->tx;
	arr[0].len    = pencode->len_tx;
    }
    if (pencode->rx && pencode->len_rx) {
	arr[1].rx_buf = pencode->rx;
	arr[1].len    = pencode->len_rx;
    }

    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);

    printf("tx: %d byte", arr[0].len);
    PrintfMemory((uint8_t *)arr[0].tx_buf, arr[0].len);
    printf("\r\nrx: %d byte", arr[0].len);
    PrintfMemory((uint8_t *)arr[1].rx_buf, arr[1].len);
}

unsigned int this_fpga_raw(struct fpga_dev    *dev,
                           struct fpga_encode *pencode)
{
    struct spi_ioc_transfer arr[] = {
	{
		.tx_buf      = (unsigned long)NULL,
		.rx_buf      = (unsigned long)NULL,
		.len         = 0,
		.cs_change   = 0,
		.delay_usecs = 100,
	},
	{
		.tx_buf    = (unsigned long)NULL,
		.rx_buf    = (unsigned long)pencode->rx,
		.len       = 0,
		.cs_change = 1,
	}
    };

    if (pencode->tx && pencode->len_tx) {
	arr[0].tx_buf = pencode->tx;
	arr[0].len    = pencode->len_tx;
    }
    if (pencode->rx && pencode->len_rx) {
	arr[1].rx_buf = pencode->rx;
	arr[1].len    = pencode->len_rx;
    }

    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);


    // printf("-- txbuf %x len tx %d\r\n", pencode->tx, pencode->len_tx);
    // printf("-- rxbuf %x len rx %d\r\n", pencode->rx, pencode->len_rx);

    // if (pencode->tx && pencode->len_tx >= 10) {
    // 	_show_cmd_name(pencode->tx + 9);
    // }
    printf("tx: %d byte", arr[0].len);
    PrintfMemory((uint8_t *)arr[0].tx_buf, arr[0].len);
    printf("\r\nrx: %d byte", arr[1].len);
    PrintfMemory((uint8_t *)arr[1].rx_buf, arr[1].len);
}
/*
自定义
tx_head 自定义头，无特殊需求这里填写NULL
tx_cksum 自定义校验码，无特殊需求这里填写NULL
*/
unsigned int this_fpga_build(
	struct fpga_dev    *dev,
	struct fpga_encode *pencode)
{
    /*
    防止 struct fpga_2016 被编译器 n字节对齐，导致长度大于MIN_TX_PACK_SIZE
    末尾的 unsigned short 是校验码
    */
    // memset(rbuf, 0xbb, rlen);
    // assert(sizeof(struct fpga_2016) + sizeof(unsigned short) == MIN_TX_PACK_SIZE);
    // WDG_CLEAR();

    char                    tx_head[sizeof(struct fpga_2016)];
    struct fpga_2016       *ptx     = (struct fpga_2016 *)tx_head;
    unsigned short          cksum   = 0;
    char                    unuse[] = { 0xaf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa };
    struct spi_ioc_transfer arr[]   = {
        {
		  .tx_buf      = (unsigned long)ptx,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = sizeof(struct fpga_2016),
		  .cs_change   = 0,
		  .delay_usecs = 100,
        },
        {
		  .tx_buf      = (unsigned long)pencode->tx,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = 0,
		  .cs_change   = 0,
		  .delay_usecs = 10000,
        },
        {
		  .tx_buf      = (unsigned long)&cksum,
		  .rx_buf      = (unsigned long)NULL,
		  .len         = 2,
		  .cs_change   = 0,
		  .delay_usecs = 10000,  // 发送完后给 FPGA 响应时间，再读取
        },
        {
		  .tx_buf    = (unsigned long)NULL,
		  .rx_buf    = (unsigned long)pencode->rx,
		  .len       = 0,
		  .cs_change = 1,
        },
    };

    // ptx->head[0] = 'G', ptx->head[1] = 'L', ptx->head[2] = 'I', ptx->head[3] = 'N', ptx->head[4] = 'K';
    // ptx->unuse0 = 0;
    // ptx->unuse1 = 0;
    // ptx->addr   = 0;
    // ptx->len    = 10 + tlen;
    // ptx->cmd    = cmd;
    // cksum       = cmd;
    // cksum += _tx_checksum((uint8_t *)tbuf, tlen);


    if (pencode->len_head != 10) {
	printf("tx-head 必须10字节\r\n");
	return -1;
    }
    memcpy(ptx, pencode->head, sizeof(struct fpga_2016));
    cksum = ptx->cmd;
    cksum += _tx_checksum((uint8_t *)pencode->tx, pencode->len_tx);


    if (1) {
	printf("*****************************");
	_show_cmd_name(ptx->cmd);
	printf("tx: %d byte", sizeof(struct fpga_2016));
	PrintfMemory((uint8_t *)ptx, sizeof(struct fpga_2016));
	printf("buf: %d byte", pencode->len_tx);
	PrintfMemory((uint8_t *)pencode->tx, pencode->len_tx);
	printf("cksum: %d byte", 2);
	PrintfMemory((uint8_t *)&cksum, 2);
    }


    // ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);
    ioctl(dev->fd, SPI_IOC_MESSAGE(ARRAY_SIZE(arr)), &arr);

    int rlen = pencode->len_rx;
    if (1) {
	if (rlen > 128) {
	    rlen = 128;
	}
	PrintfMemory((uint8_t *)pencode->rx, rlen);
    }


    return -1;
}


int all_is_hex(char *in_str, bool isprint)
{
    int   len = strlen(in_str);
    char *p   = in_str;

    for (int i = 0; i < len; i++) {
	if ((*(p + i) >= '0' && *(p + i) <= '9') ||
	    (*(p + i) >= 'a' && *(p + i) <= 'f') ||
	    (*(p + i) >= 'A' && *(p + i) <= 'F') ||
	    *(p + i) == ' ') {
	}
	else {
	    if (isprint) {
		printf("\r\n%s\r\n", in_str);
		for (int j = i; j > 0; j--) {
		    putchar(' ');
		}
		printf("^\r\n");
	    }
	    return -1;
	}
    }
    return 0;
}
int str_to_array(char *in_str, char *out_data, int max_len)
{
    int           len, count;
    char	 *p     = in_str;
    char	 *pnext = p;
    unsigned char data;

    count = 0;
    while (pnext && *pnext) {
	data = strtoul(p, &pnext, 16);
	p    = pnext;
	// printf("1data %x\r\n", data);
	count++;
    }
    // printf("count %d\r\n", count);

    count = 0;

    p     = in_str;
    pnext = p;
    while (pnext && *pnext) {
	data = strtoul(p, &pnext, 16);
	p    = pnext;

	*out_data++ = data;
	count++;
	// printf("data %x\r\n", data);
    }
    return count;
}


void loadfromfile(char *filename, char *buf, int len)
{
    FILE *fp;
    fp = fopen(filename, "rb");
    if (fp == NULL) {
	return;
    }
    int rlen  = fread(buf, 1, len, fp);
    buf[rlen] = '\0';
    fclose(fp);
}

static int _filesize(char *filename)
{
    FILE *fp;
    int   size;

    fp = fopen(filename, "rb");
    if (fp == NULL) {
	return -1;
    }

    fseek(fp, 0, SEEK_END);
    size = ftell(fp);
    fclose(fp);
    return size;
}


cJSON *loadjson(char *file_name)
{
    char  strout[2024];
    char *pstrout = strout;
    char *p;
    int   size;

    size = _filesize(file_name);
    // printf("size %d\r\n", size);
    pstrout = (char *)malloc(size + 8);
    if (0 == pstrout) {
	return NULL;
    }
    loadfromfile(file_name, pstrout, size);
    cJSON *pJsonInput = cJSON_Parse(pstrout);
    cJSON *pJsonArr;
    cJSON *pJsonIndex;

    p = cJSON_Print(pJsonInput);
    // printf("p %s\r\n", p);
    free(pstrout);

    return pJsonInput;
}


void do_send()
{
}

int check_json_is_hex(cJSON *json, char *item_name, bool isprint)
{
    if (json) {
	if (-1 == all_is_hex(json->valuestring, isprint)) {
	    if (isprint) {
		printf("错误：子项不是16进制，请检查配置文件： [%s]\r\n", item_name);
	    }
	    return -1;
	}
    }
    return 0;
}

int check_json_param(cJSON *proot, int size, struct fpga_encode *pencode, int (*pfun)(void *))
{
    int    err = 0;
    cJSON *pJindex, *ptext;
    cJSON *pJtxhead, *pJtxcksum, *pJtxdata, *pJtx, *pJrx, *pJrxlen;
    bool   bprint = false;

    if (pencode == NULL) {
	bprint = true;
    }
    for (int i = 0; i < size; i++) {
	pJindex = cJSON_GetArrayItem(proot, i);
	ptext   = cJSON_GetObjectItem(pJindex, "text");


	pJtxhead  = cJSON_GetObjectItem(pJindex, "tx-head");
	pJtxcksum = cJSON_GetObjectItem(pJindex, "tx-cksum");
	pJtxdata  = cJSON_GetObjectItem(pJindex, "tx-data");
	pJtx      = cJSON_GetObjectItem(pJindex, "tx");
	pJrx      = cJSON_GetObjectItem(pJindex, "rx");
	pJrxlen   = cJSON_GetObjectItem(pJindex, "rxlen");


	err += check_json_is_hex(pJtx, ptext->valuestring, bprint);
	err += check_json_is_hex(pJrx, ptext->valuestring, bprint);
    }
    return err;
}


int loop_every_json_param(cJSON *proot, int size, struct fpga_encode *pencode, int (*pfun)(void *))
{
    int    err = 0;
    cJSON *pJindex, *ptext;
    cJSON *pJtxhead, *pJtxcksum, *pJtxdata, *pJtx, *pJrx, *pJrxlen;
    bool   bprint = false;

    if (pencode == NULL) {
	bprint = true;
    }
    for (int i = 0; i < size; i++) {
	pJindex = cJSON_GetArrayItem(proot, i);
	ptext   = cJSON_GetObjectItem(pJindex, "text");


	pJtxhead  = cJSON_GetObjectItem(pJindex, "tx-head");
	pJtxcksum = cJSON_GetObjectItem(pJindex, "tx-cksum");
	pJtxdata  = cJSON_GetObjectItem(pJindex, "tx-data");
	pJtx      = cJSON_GetObjectItem(pJindex, "tx");
	pJrx      = cJSON_GetObjectItem(pJindex, "rx");
	pJrxlen   = cJSON_GetObjectItem(pJindex, "rxlen");
	err += check_json_is_hex(pJtx, ptext->valuestring, bprint);
	err += check_json_is_hex(pJrx, ptext->valuestring, bprint);
	if (!pencode || !pfun) {
	    continue;
	}
	if (pJtx) {
	    pencode->len_tx = str_to_array(pJtx->valuestring, pbuf_tx, MALLOC_LEN);
	    pencode->tx     = pbuf_tx;
	}
	else {
	    pencode->len_tx = 0;
	    pencode->tx     = NULL;
	}


	if (pJtxhead) {
	    pencode->len_head = str_to_array(pJtxhead->valuestring, pbuf_tx_head, MALLOC_LEN);
	    pencode->head     = pbuf_tx_head;
	}
	else {
	    pencode->len_head = 0;
	    pencode->head     = NULL;
	}


	pencode->len_rx = pJrxlen ? atoi(pJrxlen->valuestring) : MIN_RX_PACK_SIZE;
	pencode->rx     = pbuf_rx;

#if 0  // DEBUG
		printf("txbuf %x len tx %d\r\n", pencode->tx, pencode->len_tx);
		printf("rxbuf %x len rx %d\r\n", pencode->rx, pencode->len_rx);
#endif
	printf("\r\n    %s\r\n=========================================\r\n",
	       ptext->valuestring);
	pfun(pencode);
    }
    return err;
}

void do_send_to(void *pdata)
{
    struct fpga_encode *pencode = (struct fpga_encode *)pdata;
    struct fpga_encode  encode  = { 0 };
    // 完全自定义tx、rx
    if (pencode->head) {
	this_fpga_build(dev, pencode);
    }
    else if (pencode->tx) {
	this_fpga_raw(dev, pencode);
    }
    else {
	printf("JSON必须有tx或tx-head\r\n\r\n");
    }
}
int do_json(cJSON *json_root)
{
    cJSON *json_items = cJSON_GetObjectItem(json_root, "items");
    int    size;

    if (!json_items) {
	return;
    }
    size = cJSON_GetArraySize(json_items);
    struct fpga_encode encode;
    int                err;

    memset(&encode, 0, sizeof(struct fpga_encode));
    if (0 != check_json_param(json_items, size, NULL, NULL)) {
	printf("无法继续执行\r\n");
	return -1;
    }
    loop_every_json_param(json_items, size, &encode, do_send_to);
}


void open_device(char *devname)
{

    int ret;
    ret = fpga_open(devname, &dev, 6000000);

    switch (ret) {
    case 0:
	printf("open device success!\n");
	break;
    case -1:
	printf("bus error\n");
	return -1;
    case -2:
	printf("unknow device ID\n");
	return -1;
    }
}
void init_mem()
{
    pbuf_tx_head  = malloc(MALLOC_LEN);
    pbuf_tx_cksum = malloc(MALLOC_LEN);
    pbuf_tx_data  = malloc(MALLOC_LEN);
    pbuf_tx       = malloc(MALLOC_LEN);
    pbuf_rx       = malloc(MALLOC_LEN);
}
int main(int argc, char **argv)
{
    if (argc < 3) {
	return -1;
    }
    init_mem();

    char   str[] = "9f 12 11 14 15";
    char  *p     = str;
    char  *pnext;
    int    a;
    cJSON *pJson;
    open_device(argv[1]);
    pJson = loadjson(argv[2]);
    if (NULL == pJson) {
	printf("JSON 解析错误\r\n");
    }

    do_json(pJson);


    return 0;
    if (-1 == all_is_hex(str, true)) {
	printf("请检查配置文件\r\n");
	return 0;
    }

    char *pdata = malloc(1000);
    int   i, count = str_to_array(str, pdata, 40);

    for (i = 0; i < count; i++) {
	printf("%x ", pdata[i]);
    }
    printf("count = %d\r\n", count);
    return 0;
    // str[2] = '-';
    // a = strtoul("0x3", 0 , 0);
    // printf("a %x\r\n", a);
    // printf("str %s\r\n", str);

    int offset;

    offset = strtoul(p, &pnext, 16);
    printf("offset %d\r\n", offset);
    printf("ptr %s\r\n", p);
    printf("ptr %s\r\n", pnext);

    p      = pnext;
    offset = strtoul(p, &pnext, 16);
    printf("offset %d\r\n", offset);
    printf("ptr %s\r\n", p);
    printf("ptr %s\r\n", pnext);


    p      = str;
    offset = strtoul(p, &pnext, 16);
    printf("offset %d\r\n", offset);
    printf("ptr %s\r\n", p);
    printf("ptr %s\r\n", pnext);

    printf("str %s\r\n", str);
}